﻿// SsdSimCmd.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <boost/program_options.hpp>
#include <string>
#include <iostream>
#include <sstream>

#include "SimFramework/Framework.h"
#include "HostComm/Ipc/MessageClient.hpp"
#include "HostComm/CustomProtocol/CustomProtocolCommand.h"
#include "../log/log.hpp"

using namespace boost::program_options;

bool parseCommandLine(int argc, const char *argv[], std::string &hardwarespecFilename, bool &interactive)
{
    try
    {
        options_description generalOptions{"General"};
        generalOptions.add_options()("help,h", "Help")("hardwarespec", value<std::string>(), "Hardware specifications")("interactive", "Interactive actions");

        variables_map vm;
        store(parse_command_line(argc, argv, generalOptions), vm);
        if (vm.count("hardwarespec"))
        {
            hardwarespecFilename = vm["hardwarespec"].as<std::string>();
        }
        else
        {
            COUT("hardwarespec is required!");
            return false;
        }
        if (vm.count("interactive"))
        {
            interactive = true;
        }
        notify(vm);
        if (vm.count("help"))
            COUT(generalOptions);
    }
    catch (const error &ex)
    {
        std::cerr << ex.what() << '\n';
        return false;
    }
    return true;
}

void printInteractiveMenu()
{
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::stringstream sstr;
    sstr << std::endl
         << "SSD Simulation Commands";
    sstr << std::endl
         << "----";
    sstr << std::endl
         << "0. Exit application";
    sstr << std::endl
         << "1. Write data";
    sstr << std::endl
         << "2. Read data";
    sstr << std::endl
         << "3. 用户协议写";
    sstr << std::endl
         << "4. 用户协议读";
    sstr << std::endl
         << "----";
    sstr << std::endl;

    std::cout << sstr.str();
    std::cout << "User input :";

}

void handleInteractiveCmd()
{
    // CustomProtocol初始化
    std::shared_ptr<MessageClient<CustomProtocolCommand>> customClient = std::make_shared<MessageClient<CustomProtocolCommand>>("SsdSimCustomProtocolServer");
    // 下载并执行SimpleFtl固件
    //{
    //    const char *simpleFtlDll{"SimpleFtl.dll"};
    //    auto msg = customClient->AllocateMessage(0, false);
    //    msg->Data.Command = CustomProtocolCommand::Code::DownloadAndExecute;
    //    memcpy(msg->Data.Descriptor.DownloadAndExecute.CodeName, simpleFtlDll, sizeof(msg->Data.Descriptor.DownloadAndExecute.CodeName));
    //    customClient->Push(msg);
    //    COUT("push DownloadAndExecute command.");
    //}
    //// 设置扇区大小和模式
    U32 sectorSizeInTransfer = 0;
    //{
    //    auto msg = customClient->AllocateMessage(0, true);
    //    msg->Data.Command = CustomProtocolCommand::Code::SetSectorSize;
    //    msg->Data.Descriptor.SectorInfoPayload.SectorInfo.SectorSizeInBit = 9;
    //    msg->Data.Descriptor.SectorInfoPayload.SectorInfo.CompactMode = true;
    //    msg->Data.Descriptor.SectorInfoPayload.SectorInfo.CompactSizeInByte = 30;
    //    customClient->Push(msg);
    //    while (!customClient->HasResponse());
    //    auto rsp = customClient->PopResponse();
    //}
    // 获取设备信息
    {
        COUT("push GetDeviceInfo cmd");
        auto msg = customClient->AllocateMessage(0, true);
        msg->Data.Command = CustomProtocolCommand::Code::GetDeviceInfo;
        customClient->Push(msg);
        while (!customClient->HasResponse())
            ;
        auto rsp = customClient->PopResponse();
        sectorSizeInTransfer = rsp->Data.Descriptor.DeviceInfoPayload.SectorInfo.CompactMode
                                   ? rsp->Data.Descriptor.DeviceInfoPayload.SectorInfo.CompactSizeInByte
                                   : (1 << rsp->Data.Descriptor.DeviceInfoPayload.SectorInfo.SectorSizeInBit);
        COUT("GetDeviceInfo" << rsp->PayloadSize);
    }
    U32 loop_cnt = 0;
    bool exit = false;
    Sleep(100);
    while (!exit)
    {
        int userSelectedCmd = -1;

        printInteractiveMenu();
        std::cin >> userSelectedCmd;
        std::shared_ptr<MessageClient<SimFrameworkCommand>> client = std::make_shared<MessageClient<SimFrameworkCommand>>("SsdSimMainMessageServer");
        if (0 == userSelectedCmd)
        {
            Message<SimFrameworkCommand> *message = client->AllocateMessage(sizeof(SimFrameworkCommand), false);
            message->Data.Code = SimFrameworkCommand::Code::Exit;
            client->Push(message);
            exit = true;
        }
        else if (1 == userSelectedCmd)
        {
            // Write data，内容固定为"abc"
            const char *inputData = "abc";
            size_t dataSize = strlen(inputData);
            Message<SimFrameworkCommand> *message = client->AllocateMessage(sizeof(SimFrameworkCommand) + dataSize, false);
            message->Data.Code = SimFrameworkCommand::Code::DataInLoopback;
            memcpy(message->Payload, inputData, dataSize);
            message->PayloadSize = dataSize;
            client->Push(message);
            auto response = client->PopResponse();
            COUT("Write command sent. Response received.");
            if (response && response->PayloadSize > 0)
            {
                std::string respData(reinterpret_cast<char *>(response->Payload), response->PayloadSize);
                COUT("响应内容: " << respData);
            }
            else
            {
                COUT("响应为空。");
            }
        }
        else if (2 == userSelectedCmd)
        {
            // Read data
            Message<SimFrameworkCommand> *message = client->AllocateMessage(sizeof(SimFrameworkCommand), false);
            message->Data.Code = SimFrameworkCommand::Code::DataOutLoopback;
            client->Push(message);
            auto response = client->PopResponse();
            if (response && response->PayloadSize > 0)
            {
                std::string readData(reinterpret_cast<char *>(response->Payload), response->PayloadSize);
                COUT("Read data: " << readData);
            }
            else
            {
                COUT("No data read or error.");
            }
        }
        else if (3 == userSelectedCmd)
        {
            constexpr U32 lba = 123;
            U32 sectorCount = 1;
            U32 payloadSize = sectorCount * sectorSizeInTransfer;
            // 用户协议写
            auto writeMessage = customClient->AllocateMessage(payloadSize, true);
            for (decltype(sectorCount) i{0}; i < sectorCount; ++i)
            {
                auto buffer = &(static_cast<U8 *>(writeMessage->Payload)[i * sectorSizeInTransfer]);
                memset((void *)buffer, loop_cnt, sectorSizeInTransfer);
            }
            writeMessage->Data.Command = CustomProtocolCommand::Code::Write;
            writeMessage->Data.Descriptor.SimpleFtlPayload.Lba = lba;
            writeMessage->Data.Descriptor.SimpleFtlPayload.SectorCount = sectorCount;
            customClient->Push(writeMessage);
            while (!customClient->HasResponse())
                ;
            auto response = customClient->PopResponse();
            std::cout << "CustomProtocol写命令已发送，响应状态: " << (int)response->Data.CommandStatus << std::endl;
            loop_cnt++;
        }
        else if (4 == userSelectedCmd)
        {
            constexpr U32 lba = 123;
            U32 sectorCount = 1;
            U32 payloadSize = sectorCount * sectorSizeInTransfer;
            // 用户协议读
            auto readMessage = customClient->AllocateMessage(payloadSize, true);
            readMessage->Data.Command = CustomProtocolCommand::Code::Read;
            readMessage->Data.Descriptor.SimpleFtlPayload.Lba = lba;
            readMessage->Data.Descriptor.SimpleFtlPayload.SectorCount = sectorCount;
            customClient->Push(readMessage);
            while (!customClient->HasResponse())
                ;
            auto response = customClient->PopResponse();
            std::cout << "CustomProtocol读命令已发送，响应状态: " << (int)response->Data.CommandStatus << std::endl;
            if (response->Payload && response->PayloadSize > 0)
            {
                std::cout << "读回数据前8字节末8字节: ";
                for (size_t i = 0; i < std::min<size_t>(8, response->PayloadSize); ++i)
                    std::cout << std::hex << (int)((U8 *)response->Payload)[i] << " ";
                for (size_t i = (response->PayloadSize > 8 ? response->PayloadSize - 8 : 0); i < response->PayloadSize; ++i)
                    std::cout << std::hex << (int)((U8 *)response->Payload)[i] << " ";
                std::cout << std::dec << std::endl;
            }
        }
        else
        {
            std::cout << "Invalid commands" << std::endl;
        }
    }
}

int main(int argc, const char *argv[])
{
    std::string hardwarespecFilename = "";
    bool interactive = false;

    if (parseCommandLine(argc, argv, hardwarespecFilename, interactive))
    {
        if (!hardwarespecFilename.empty())
        {
            try
            {
                Framework framework;
                framework.Init(hardwarespecFilename);

                // start framework async if interative option enabled
                if (true == interactive)
                {
                    auto fwFuture = std::async(std::launch::async, &Framework::operator(), &framework);

                    // handle interactive commands
                    handleInteractiveCmd();
                }
                else // run framework in sync if no interactive option
                {
                    framework.operator()();
                }
            }
            catch (const Framework::Exception &err)
            {
                std::cout << "Error";
                std::cout << err.what();
                return -1;
            }
        }
    }

    return 0;
}
